#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>

// 命令行参数表
#define ARGV_SIZE 128
int g_argc;
char* g_argv[ARGV_SIZE];

// 环境参数表
#define ENV_SIZE 128
int g_envc;
char* g_env[ENV_SIZE];

// 进程退出码
int overcode;

//接受命令的buff
#define CMD_SIZE 256
char cmd_buffer[CMD_SIZE];

// 重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir = NONE_REDIR;
std::string filename;


void init_env()
{
    //本来要从配置文件来
    extern char** environ;
    //1. 获取环境变量
    for(int i = 0; environ[i]; i++)
    {
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envc++;
    }
    g_env[g_envc++] = (char*)"haha=666";//test
    g_env[g_envc] = NULL;
    //导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

//获取环境变量

char* get_home()
{
    return getenv("HOME");
}
char* get_user()
{
    return getenv("USER");
}

const char* get_cwd()
{
    std::string str =  getenv("PWD");
    int pos = str.rfind('/');
    //...
    std::string tmp = str.substr(pos+1);
    return tmp.c_str();
}

char* get_host()
{
    return getenv("HOSTNAME");
}

// 1.打印命令提示行
void print_command()
{
    redir = NONE_REDIR;
    printf("[%s@%s %s]# ", get_user(), get_host(), get_cwd());
}

// 2.接受命令
bool get_command()
{
    char* ret = fgets(cmd_buffer,CMD_SIZE,stdin);
    if(ret == NULL)
        return false;
    // \n的处理
    cmd_buffer[strlen(cmd_buffer)-1] = 0;
    if(strlen(cmd_buffer) == 0)
        return false;
    return true;
}


void trim_space(int& i)
{
    while(isspace(cmd_buffer[++i]));
}

// 3. 重定向分析 "ls -a -l > file.txt" -> "ls -a -l" "file.txt" -> 判定重定向方式
void redir_check()
{
    filename.clear();

    // 检查是否有 >   >>   <
    int n = strlen(cmd_buffer);
    for(int i = n-1; i > 0; i--)
    {
        if(cmd_buffer[i] =='>' || cmd_buffer[i] == '<')
        {
             if(cmd_buffer[i] == '>' && cmd_buffer[i-1] =='>')
             {
                 redir = APPEND_REDIR;
                 cmd_buffer[i-1] = 0;
             }
             else if(cmd_buffer[i] == '>')
             {
                 redir = OUTPUT_REDIR;
             }
             else if(cmd_buffer[i] == '<')
             {
                 redir = INPUT_REDIR;
             }
             cmd_buffer[i] = 0;
             trim_space(++i);
             filename += cmd_buffer+i;
             break;
         }
    }
}

// 4.分析命令---将命令放入到命令行参数表中
bool anaiyse_command()
{
    g_argc = 0;
    //遇到空格就切分
    g_argv[g_argc++] =  strtok(cmd_buffer," ");
    //if(g_argv[g_argc-1]!=NULL)
    while((bool)(g_argv[g_argc++] = strtok(NULL, " ")));
    g_argc--;
    return true;
}

void echo()
{
    if(g_argc==2)
    {
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            //查看上一进程退出码
            printf("%d\n",overcode);
        }
        else if(opt[0] == '$')
        {
            // 查看环境变量
            std::string envname = opt.substr(1);

            char* str = getenv(envname.c_str());
            if(str)
                std::cout<<str<<std::endl;
        }
        else{
            std::cout<<opt<<std::endl;
        }
        overcode = 0;
    }

}

void cd()
{
    if(g_argc == 1)
    {
        char* cwd = get_home();
        chdir(cwd);
        // 改变环境变量中的PWD
        setenv("PWD", cwd, 1);
    }
    else{
        std::string where = g_argv[1];
        // cd - / cd ~
        if(where == "-")
        {
            //...    
        }
        else if(where == "~")
        {
            //...
        }
        else
        {
            //...
        }
    }
}

// 5.查看是否是内建命令
bool check_is_builtin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        cd();
        return true;
    }
    else if(cmd == "echo")
    {
        echo();
        return true;
    }
    else if(cmd == "export")
    {
    }
    else if(cmd == "alias")
    {
    }

    return false;
}

// 6.执行命令
void execute()
{
    // child
    pid_t id = fork();
    if(id<0)
        exit(1);
    else if(id==0)
    {
        if(redir == INPUT_REDIR)
        {
            int fd = open(filename.c_str(), O_RDONLY);
            if(fd<0)exit(1);
            dup2(fd,0);
            close(fd);
        }
        else if(redir == OUTPUT_REDIR)
        {
            int fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0666);
            if(fd<0)exit(1);
            dup2(fd,1);
            close(fd);
        }
        else if(redir == APPEND_REDIR)
        {
            int fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_APPEND, 0666);
            if(fd<0)exit(1);
            dup2(fd,1);
            close(fd);
        }

        // child进程切换
        execvp(g_argv[0],g_argv);
        exit(1);
    }
    
    // 父进程等待
    int status = 0;
    pid_t cid = waitpid(id, &status, 0);
    if(cid > 0)
    {
        overcode = WEXITSTATUS(status);
    }
}

void print()
{
    for(int i = 0; g_argv[i]; i++)
    {
        printf("g_argv[%d]->%s\n",i,g_argv[i]);
    }
    printf("%d\n",g_argc);
}

int main()
{
    init_env();
    
    while(true)
    {
         // 1.打印命令提示行
         print_command();
         
         // 2.接受命令
         if(!get_command())
             continue;

         // 3. 重定向分析 "ls -a -l > file.txt" -> "ls -a -l" "file.txt" -> 判定重定向方式
         redir_check();

         // 4.分析命令---将命令放入到命令行参数表中
         if(!anaiyse_command())
            continue;
        
         print();
         // 5.查看是否是内建命令、别名...
         if(check_is_builtin())
             continue;

         // 6.执行命令
         execute();
    }
     return 0;
}
